import { app, protocol, screen, shell, session, dialog, BrowserWindow, Tray, Menu, ipcMain, nativeImage, globalShortcut } from 'electron'
import type { IpcMainEvent, IpcMainInvokeEvent, OpenDialogOptions } from 'electron'
import { merge, mergeWith, isArray } from 'lodash-es'

import { fileURLToPath } from 'node:url'
import https from 'node:https'
import path from 'node:path'
import fs from 'node:fs'
import { randomUUID } from 'node:crypto'

import { LocalStorage } from './storage'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

// The built directory structure
//
// ├─┬─┬ dist
// │ │ └── index.html
// │ │
// │ ├─┬ dist-electron
// │ │ ├── main.js
// │ │ └── preload.mjs
// │
process.env.APP_ROOT = path.join(__dirname, '..')

// 🚧 Use ['ENV_NAME'] avoid vite:define plugin - Vite@2.x
export const VITE_DEV_SERVER_URL = process.env['VITE_DEV_SERVER_URL']
export const MAIN_DIST = path.join(process.env.APP_ROOT, 'dist-electron')
export const RENDERER_DIST = path.join(process.env.APP_ROOT, 'dist')

process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path.join(process.env.APP_ROOT, 'public') : RENDERER_DIST

const ICON_PATH = path.join(process.env.VITE_PUBLIC, 'favicon.png')
const USER_DATA_PATH = app.getPath('userData')

const storage = new LocalStorage(path.join(USER_DATA_PATH, 'config.json'), app.getVersion())

const gotTheLock = app.requestSingleInstanceLock({ myKey: `fish-ads-app-${app.getVersion()}` })

let win: BrowserWindow | null
let tray: Tray | null

function createWindow() {

  initIpcMain()

  let config = listener.readConfig(undefined)

  win = new BrowserWindow({
    width: 920,
    height: 732,
    frame: false,
    alwaysOnTop: config.alwaysOnTop,
    transparent: true,
    kiosk: false,
    icon: ICON_PATH,
    webPreferences: {
      webviewTag: true,
      preload: path.join(__dirname, 'preload.mjs'),
      nodeIntegration: true,
      contextIsolation: true
    },
  })

  if (config.alwaysOnTop) {
    win.setAlwaysOnTop(true, config.alwaysOnTopLevel || 'floating')
  }

  // Create a system tray
  const icon = nativeImage.createFromPath(ICON_PATH),
    showWin = () => {
      if (!win) return
      win.isVisible() ? win.focus() : win.show()
      if (!win?.isAlwaysOnTop()) {
        win.webContents.executeJavaScript(`
          window.F && window.F.visible && (window.F.hidden && window.F.toggleAds())
        `, true)
      }
    }
  tray = new Tray(icon)
  tray.setContextMenu(Menu.buildFromTemplate([
    { label: '显示（双击图标效果相同）', click: showWin },
    { label: '切换置顶（临时生效）', click: () => win?.setAlwaysOnTop(!win.isAlwaysOnTop(), config.alwaysOnTopLevel || 'floating') },
    { label: '退出', click: listener.closeAllWindows },
  ]))
  tray.setToolTip('右键呼出菜单')
  tray.on('double-click', showWin)
  // End tray

  listener.loadHash(undefined, '/')

  win.on('blur', () => {
    !win?.isAlwaysOnTop() && win?.webContents.executeJavaScript(`
      window.F && window.F.visible && (!window.F.hidden && window.F.toggleAds())
    `, true)
  })

  globalShortcut.register('Ctrl+Alt+D', () => {
    listener.closeAllWindows()
  })

  globalShortcut.register('Ctrl+Alt+T', () => {
    let config = listener.readConfig(undefined)

    win?.setAlwaysOnTop(!win.isAlwaysOnTop(), config.alwaysOnTopLevel || 'floating')
    if (win?.isAlwaysOnTop()) {
      win.webContents.executeJavaScript(`
          window.F && window.F.visible && (window.F.hidden && window.F.toggleAds())
        `, true)
    }
    win?.focus()
  })

  globalShortcut.register('Ctrl+Alt+F12', () => {
    win?.webContents.openDevTools()
  })

  // Disabled for the bytedance dispatch_message protocol
  protocol.handle('bytedance', () => {
    return new Response(null, { status: 200 })
  })
}

const listener = {
  loadLocalFile: async (_: IpcMainInvokeEvent, filePath: string) => {
    if (!fs.existsSync(filePath)) return null

    const ext = path.extname(filePath).toLowerCase()
    if (ext === '.png' || ext === '.jpg' || ext === '.jpeg' || ext === '.gif') {
      return {
        ext: ext,
        base64: fs.readFileSync(filePath, 'base64')
      }
    }

    return {
      ext: ext,
      content: fs.readFileSync(filePath, 'utf-8'),
    }
  },
  selectLocalFile: async (_: IpcMainInvokeEvent, options: OpenDialogOptions, copy: boolean) => {
    const result = await dialog.showOpenDialog(win!, options)

    if (result.canceled) return null

    let filePath = result.filePaths[0]

    try {
      if (copy) {
        const copyDir = path.join(USER_DATA_PATH, 'wechat_resource')
        const copyFilePath = path.join(copyDir, randomUUID() + path.extname(filePath))
        if (!fs.existsSync(copyDir)) {
          fs.mkdirSync(copyDir, { recursive: true })
        }
        fs.copyFileSync(filePath, copyFilePath)
        filePath = copyFilePath
      }
      const file = await listener.loadLocalFile(_, filePath)
      return merge({ path: filePath }, file)
    } catch (error) {
      console.error('读取文件失败：', error)
      return null
    }
  },
  loadExtension: async (_: IpcMainEvent) => {
    const config = listener.readConfig(undefined)

    if (config.extensionPath) {
      const extensions = session.defaultSession.getAllExtensions()
      if (extensions.length <= 0) {
        const extension = session.defaultSession.loadExtension(config.extensionPath, { allowFileAccess: true })
        dialog.showMessageBox(win!, {
          type: 'info',
          title: '扩展程序初始化',
          message: `“${(await extension).name}” 已经加载成功`,
          buttons: ['我已知晓'],

        })
      }
    }
  },
  selectExtensionFolder: async (_: IpcMainInvokeEvent, flag: boolean) => {
    if (flag) {
      const result = await dialog.showOpenDialog(win!, { properties: ['openDirectory'] })

      if (result.canceled) {
        return { status: false, msg: '取消选择' }
      } else {
        const extension = await session.defaultSession.loadExtension(result.filePaths[0], { allowFileAccess: true }).then(item => {
          return { status: true, msg: `插件 ${item.name} 已加载`, data: result.filePaths[0] }
        }).catch(error => {
          return { status: false, msg: error.message }
        })
        return extension
      }
    } else {
      session.defaultSession.getAllExtensions().forEach(extension => {
        session.defaultSession.removeExtension(extension.id)
      })
      return { status: true, msg: '插件已移除，请重新选择', data: '' }
    }
  },
  updateInsertJs: (event: IpcMainEvent) => {
    const config = listener.readConfig(undefined)
    const url = path.join(new URL(config.source).origin, '/assets/ads/insert.js')

    const tempPath = path.join(process.resourcesPath, 'insert.tmp')
    const file = fs.createWriteStream(tempPath)

    if (app.isPackaged) {
      https.get(url, (response) => {
        if (response.statusCode !== 200) {
          fs.unlinkSync(tempPath)
          throw new Error(`请求失败，状态码: ${response.statusCode}`)
        }

        response.pipe(file)

        file.on('finish', () => {
          file.close(() => {
            fs.renameSync(tempPath, path.join(process.resourcesPath, 'insert.js'))
          })
          event.reply('update-complete')
        })
      }).on('error', (err) => {
        if (fs.existsSync(tempPath)) {
          fs.unlinkSync(tempPath)
        }
        event.reply('update-error', err.message)
      })
    } else {
      event.reply('update-error', '未打包程序！')
    }
  },
  readConfig: (_: IpcMainInvokeEvent | undefined) => {
    let config = storage.read() || storage.DEFAULT
    if (config.version !== storage.DEFAULT.version) {
      config.version = storage.DEFAULT.version
      config.read = false
    }
    if (!config.source) {
      config.source = storage.DEFAULT.source
      config.home = storage.DEFAULT.home
      config.verticalWebsiteHost = storage.DEFAULT.verticalWebsiteHost
    }
    return config
  },
  writeConfig: (_: IpcMainEvent | undefined, config: any) => {
    storage.write(config)
    win?.setAlwaysOnTop(config.alwaysOnTop || false, config.alwaysOnTopLevel || 'floating')
  },
  setConfig: (_: IpcMainEvent | undefined, obj: any) => {
    let config = listener.readConfig(undefined)
    mergeWith(config, obj, (objValue, srcValue) => {
      if (isArray(objValue) && isArray(srcValue)) {
        if (srcValue.length > 0) {
          return srcValue
        } else { // 防止数组合并时清空不掉的情况
          return []
        }
      }
      return undefined
    })
    storage.write(config)
  },
  openResourceFolder: (_: IpcMainEvent | undefined, path: string) => {
    if (win?.isAlwaysOnTop()) { // 置顶影响观感
      win?.setAlwaysOnTop(false)
    }
    shell.openPath(path || USER_DATA_PATH)
  },
  loadHash: (_: IpcMainEvent | undefined, hash: string) => {
    if (hash === '/wechat') {
      win?.setSkipTaskbar(true)
    } else {
      win?.setSkipTaskbar(false)
    }

    if (VITE_DEV_SERVER_URL) {
      win?.loadURL(VITE_DEV_SERVER_URL + '#' + hash)
    } else {
      // win.loadFile('dist/index.html')
      win?.loadFile(path.join(RENDERER_DIST, 'index.html'), {
        hash: hash
      })
    }
  },
  togglePopup: (_: IpcMainEvent, flag: boolean) => {
    let primaryDisplay = screen.getPrimaryDisplay()
    let { width, height } = primaryDisplay.workAreaSize
    if (flag) {
      let config = listener.readConfig(undefined)
      if (config.position === 'left') {
        win?.setBounds({ x: 10, y: height - (470 + 10), width: 370, height: 470 })
      } else {
        win?.setBounds({ x: width - (370 + 10), y: height - (470 + 10), width: 370, height: 470 })
      }
      win?.setSkipTaskbar(true)
    } else {
      win?.setBounds({ width: 920, height: 732 })
      win?.setSkipTaskbar(false)
      win?.center()
    }
  },
  minimize: (_: IpcMainEvent) => {
    win?.minimize()
  },
  fullScreen: (_: IpcMainEvent, flag: boolean) => {
    win?.setFullScreen(flag)
  },
  closeAllWindows: () => {
    BrowserWindow.getAllWindows().forEach(win => win.close())
  }
}

function initIpcMain() {
  ipcMain.handle('load-local-file', listener.loadLocalFile)
  ipcMain.handle('select-local-file', listener.selectLocalFile)
  ipcMain.on('load-extension', listener.loadExtension)
  ipcMain.handle('select-extension-folder', listener.selectExtensionFolder)
  ipcMain.on('update-insert-js', listener.updateInsertJs)
  ipcMain.handle('read-config', listener.readConfig)
  ipcMain.on('write-config', listener.writeConfig)
  ipcMain.on('set-config', listener.setConfig)
  ipcMain.on('open-resource-folder', listener.openResourceFolder)
  ipcMain.on('load-hash', listener.loadHash)
  ipcMain.on('toggle-popup', listener.togglePopup)
  ipcMain.on('minimize', listener.minimize)
  ipcMain.on('full-screen', listener.fullScreen)
  ipcMain.on('toggle-always-on-top', () => {
    let config = listener.readConfig(undefined)
    win?.setAlwaysOnTop(!win.isAlwaysOnTop(), config.alwaysOnTopLevel || 'floating')
  })
  ipcMain.on('escape', () => {
    const url = new URL(win?.webContents.getURL() || ''), hash = url.hash
    if (win?.isFocused()) {
      if (hash === '#/wechat') {
        win.webContents.executeJavaScript(`window.F.hidden = false`, true)
      }
      win?.hide()
    }
  })
  ipcMain.once('close-all-windows', listener.closeAllWindows)
}

// 禁用硬件加速
// app.disableHardwareAcceleration()

if (!gotTheLock) {
  app.quit()
} else {
  app.on('web-contents-created', (_, contents) => {
    if (contents.getType() === 'webview') {
      contents.setWindowOpenHandler(({ url }) => {
        contents.loadURL(url)
        return { action: 'deny' }
      })
    }
  })

  app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
      app.quit()
      win = null
    }
  })

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow()
    }
  })

  app.whenReady().then(createWindow)
}
